Galileo Computing <openbook>
Galileo Computing - Programming the Net
Galileo Computing - Programming the Net

...powered by www.netzwerkartist.de...

Java 2 von Friedrich Esser
Designmuster und Zertifizierungswissen
Zum Katalog
gp Kapitel 1 Grundlagen
  gp 1.1 Java-Überblick
  gp 1.2 Programmstruktur
  gp 1.3 Primitive Datentypen
  gp 1.4 Lexikalische Grundlagen
  gp 1.5 Konvertierung primitiver Typen
  gp 1.6 Initialisierung von Variablen
  gp 1.7 Namenskonventionen
  gp 1.8 Zusammenfassung
  gp 1.9 Testfragen

Kapitel 1 Grundlagen

Nach einem Überblick über die »etwas andere« Java-Technologie werden die Details zu den grundlegenden Bausteinen in möglichst einfachen Regeln zusammengefasst.
Konventionen und Stile sind grundlegende Bausteine einer Sprache, also schließt das Kapitel mit Namenskonventionen ab.

Die Zusammenstellung dieser Grundlagen orientiert sich an den Anforderungen der Zertifizierung (siehe Vorwort) und hebt dabei besonders die Aspekte hervor, die Java von C/C++ unterscheiden.

Damit ein weitgehend goto-freies Lesen möglich ist, werden verwendete Begriffe wie Interface, Ausnahmen, Array etc. kurz vorgestellt, obwohl sie erst in späteren Kapiteln ausführlich behandelt werden.

Testfragen am Ende des Kapitels

Anhand der abschließenden Fragen kann geprüft werden, ob das Basiswissen für einfache Zertifizierungsfragen ausreicht, und ggf. sollte der zugehörige Abschnitt nachgearbeitet werden.


Galileo Computing

1.1 Java-Überblick  downtop

Java-Technologie: Sprache, Plattform, JVM

Das Wort Java steht für eine Technologie, die drei zentrale Komponenten umfasst:

gp  die eigentliche Java-Programmiersprache, die Syntax und Semantik festlegt.
gp  die Java-Plattform, kurz J2SE (Java 2 Standard Edition), die auf Basis der Programmiersprache ein umfassendes Klassensystem bereitstellt, um Java-Anwendungen unabhängig vom API des Betriebssystems zu programmieren.
gp  die Java Virtual Maschine (JVM), die Java-Code von der realen Maschine unabhängig macht, d.h. den Java-Byte-Code ausführt. Vom Betriebssystem bleibt die JVM natürlich abhängig.

Die Java-Sprache selbst ist seit der Einführung 1995 bemerkenswert stabil. Aufgrund der Erfahrungen mit C/C++, Smalltalk und Oberon war das Design bereits so ausgereift, dass die Sprache nachfolgend kaum geändert wurde, was man allerdings von den beiden anderen Komponenten (Plattform, JVM) nicht unbedingt behaupten kann.

Die Evolution der Java-Plattform
deprecated

Obwohl die Sprache die Basis darstellt, liegt die Funktionalität von Java in der Plattform. Sie hat bis zur Version 1.3 gewaltige Änderungen bzw. Erweiterungen erfahren, die zum Teil nur mühsam in das vorherige Klassensystem eingepasst werden konnten. Die Warnung deprecated und konkurrierende Funktionalitäten in alten und neuen Klassen belegen dies.

Aufgrund der umfangreichen Änderungen wurde ab der Plattformversion 1.2 die offizielle Bezeichnung Java 2 von Sun eingeführt.

Auch die JVM hat eine ähnlich steile Entwicklung gemacht , die hauptsächlich die Effizienz der Programmausführung berührt.

Da die JVM die Verbindung zwischen Betriebssystem, Browser und Java-Plattform darstellt, ist sie der ideale Ansatzpunkt für politische Machtkämpfe.

Die Sun-Initiative »write once, run anywhere« hängt entscheidend davon ab, ob die J2SE und die zugehörige JVM in jedem Betriebssystem bzw. Browser installiert sind.

Diversifikation der Java-Plattform

Der Siegeszug von Java hat gleichzeitig eine Schattenseite. Die Java-Technologie muss für PDAs, Handys, Drucker, Smartcards oder Enterprise-Server zwangsläufig adaptiert werden.

Deswegen wurde unter der Parole »one size doesn't fit it all«, aber natürlich »one language is all you need« bei Sun mit der Diversifikation der Plattformen und der JVM begonnen (siehe auch Abb. 1.1).

Plattform-
Abhängigkeit

Obwohl die Sprache an sich davon unberührt bleibt, wird dann jeder Java-Code wieder abhängig, und zwar von der Plattform. Zumindest ein Konkurrent von Sun sieht darin prinzipiell keinen Unterschied mehr zu einer direkten Bindung einer Sprache, z.B. Java, an das API des Betriebssystems.


Abbildung
Abbildung 1.1   Java-Plattform-Architektur
Galileo Computing

1.1.1 Code-Design  downtop

Java-Programme sind nicht monolithisch, sondern bestehen aus kleinen autonomen Einheiten, den Klassen (classes). Der Compiler (z.B. javac) speichert den Byte-Code jeder Klasse in jeweils eigene Dateien. Dies erlaubt es der JVM, Klassen einzeln und nur bei Bedarf (auch über ein Netzwerk) zu laden.

Klassen als ausführbare Einheiten

Um zu starten, muss der JVM nur die Klasse angegeben werden, die sie als erste laden und ausführen soll. Dazu muss diese Startklasse natürlich, abhängig von der Umgebung - Betriebssystem, Browser oder Web-Server -die entsprechende Funktionalität besitzen.

Die Kehrseite dieses Designs besteht darin, dass erst während der Ausführung Konflikte beim Nachladen von Klassen in Verzeichnissen bzw. im Netz entstehen, die bei traditionellen Programmen in der Regel schon vorher beim Linken auftreten.

Klassen gehören immer zu einem Package, wobei Java-Packages (über ihre Namen) Hierarchien bilden können. Die J2SE enthält zurzeit ca. 1.500 Klassen, organisiert in 59 Packages.

Um zumindest für eine gewisse Zeit J2SE zu stabilisieren, werden neue Klassen in Extension Packages (wie z.B. Java3D, Java Media Framework JMF etc.) ausgelagert. Dies Pakete gehören damit allerdings auch nicht zu J2SE und müssen bei Bedarf zusätzlich installiert werden.

Interface

Java kennt bereits auf Sprachebene Interfaces.

gp  Ein Interface fasst eine Gruppe zusammengehöriger Methoden unter einem Namen zusammen, der die gemeinsame Aufgabe dieser Methoden charakterisiert.

Interfaces: Rollen, die Objekte spielen können

Ein Interface hat damit den Charakter einer Rolle, die ein reales Objekt beherrscht oder nicht. Somit sind Interfaces abstrakt, man kann von ihnen keine Objekte erzeugen.

Eine Klasse kann beliebig viele Interfaces implementieren. Jedes Objekt kann dann z.B. beim Methoden-Aufruf anstelle eines Interface-Parameters stehen, sofern die zugehörige Klasse das Interface implementiert hat.

Damit werden schwerwiegende Defizite der Vererbung unter Beibehaltung der Hauptvorteile wie Substitution behoben.

Methoden und Daten

Keine globalen Funktionen und Daten

Für C/C++-Konvertiten ist die Tatsache ungewohnt, dass es keine globalen Funktionen und Daten außerhalb von Klassen gibt. Der auszuführende Code ist immer in den Methoden (Methods) der Klassen enthalten.

Aufruf oder Zugriff mit Hilfe des
dot-operators

Da Methoden und Daten an Klassen oder deren Objekte gebunden sind, erfolgt der Aufruf einer Klassen-/Objekt-Methode oder der Zugriff auf Daten von außen über den zugehörigen Klassen- bzw. Objektnamen, separiert durch den Punkt (dot-operator) vom Methoden-/Datennamen:

          Klasse.methode()  bzw.  objekt.methode() 
          Klasse.feld       bzw.  objekt.feld

Die Klammern müssen bei Methoden immer angegeben werden, auch wenn sie keine Parameter haben. Damit ist ein Methoden-Aufruf leicht von einem Datenzugriff zu unterscheiden.

Arrays

Wie bei C/C++ werden Arrays als geordnete Kollektion von Elementen mit gleichem Typ durch eckige Klammern deklariert, wobei auch der Zugriff auf ein Element über den Index in eckigen Klammern erfolgt.

Damit sind allerdings die Gemeinsamkeiten mit C/C++ auch schon erschöpft. Denn bereits die bevorzugte Art, in Java Arrays zu deklarieren, stellt sich folgendermaßen dar:

Deklaration eines Arrays

gp  Angabe des Typs der Elemente, gefolgt von einer eckigen Klammer
           byte[] bArr;   String[] sArr;

Da die Angabe der Größe bei der Deklaration nicht erlaubt ist, kann die Array-Variable ein Array beliebiger Länge referenzieren. Diese Art der Deklaration kann auch für die Rückgabe von Arrays aus einer Methode verwendet werden (was C/C++ ohnehin nicht erlaubt).

Anlage eines Arrays mittels new

Mit der o.a. Deklaration ist noch kein Array angelegt. Dies geschieht normalerweise mittels eines new-Ausdrucks und kann direkt bei der Deklaration oder auch zu einem späteren Zeitpunkt erfolgen:

           byte[] bArr= new byte[10]; 

Die Variable bArr referenziert nun ein Array mit zehn Elementen vom Typ byte (Näheres zu byte: siehe 1.3).

Für kleine Arrays gibt es eine sehr praktische Kurzform, die neben der (impliziten) Anlage des Arrays bereits den einzelnen Array-Elementen Werte zuweist:

    int[] iArr= {1,2,3}; 

Dies ist äquivalent zu:

    int[] iArr= new int[3];
    iArr[0]=1; iArr[1]=2; iArr[2]=3;

Galileo Computing

1.1.2 Java-Applikation  downtop

Applications, Applets, Servlets

Java-Code, der direkt (mittels einer JVM) im Betriebssystem ausgeführt wird, nennt man Applikation (Application), innerhalb eines Browsers Applet und innerhalb eines Web-Servers Servlet oder Java Server Pages (JSP).

Die JVM muss bei jeder Startklasse einer Applikation natürlich einen ihr bekannten Einstiegspunkt haben. Dieser Einstiegspunkt ist die Methode main(). Sie muss immer wie folgt definiert werden:

     public static void main(String[] args) { ... 
}

Auf diese Weise kann jede Klasse zum Testen ihrer Funktionalität mittels main() (temporär) ausführbar gemacht werden, selbst wenn sie außerhalb des Packages nicht von anderen Klassen benutzt werden kann.

Beispiel einer ausführbaren Klasse

Mittels des String-Arrays args können Argumente für die Programmausführung übergeben werden:

class StartableClass {
  public static void main(String[] args) {
    // Feld length enthält die Länge des 
Arrays
    for (int i=0; i < args.length; i++)
      // Methode print() des Feldes out der Klasse 
System
      System.out.print(args[i]+" ");
  }
}

Diese Klasse kann nun - unabhängig von ihrem eigentlichen Zweck - als eigenständiges Programm, genauer als Applikation von der JVM (z.B. java) ausgeführt und getestet werden, z.B. mit vier Strings:

   java StartableClass 2 good 4 u

Dies erzeugt auf der Textkonsole das nützliche Echo :

   too good for you

Galileo Computing

1.2 Programmstruktur  downtop

Aufgrund der Konzeption von Java als Internet-Sprache war es zwangsläufig notwendig, Java mit einer konsistenten Konvention für die Programmstruktur auszustatten. Dies zeigt sich u.a. in der J2SE:

gp  Modularisierung des Codes mit Hilfe des Package-Konzepts
gp  Ein hierarchisches Namenssystem, das es ermöglicht, Namenskollisionen global zu vermeiden

Die Konvention beinhaltet eine Anleitung zur Abbildung der Package- und Klassennamen in die Verzeichnisstrukturen und Dateien unterschiedlicher Betriebssysteme. Damit müssen natürlich die Namen von Packages gültige Verzeichnisnamen bei allen Betriebssystemen sein.


Galileo Computing

1.2.1 Packages und Namespace  downtop

Package: Kollektion zusammengehöriger Klassen/Interfaces

Nach dem Vorbild von Modula werden alle Klassen in Java-Packages gekapselt. Ein Package besteht aus einer Kollektion von:

gp  Klassen
gp  Interfaces (Schnittstellen)
gp  Subpackages

Mindestens besteht es aus einem dieser Elemente. Ein Package kann also durchaus nur eine »Hülle« für Subpackages sein, d.h. selbst keine Klassen oder Interfaces enthalten.

Namespace

Ein Package bildet für die enthaltenen Mitglieder einen so genannten Namensraum (namespace).

Eindeutigkeit der Namen

Innerhalb des Namensraums müssen die Namen der Mitglieder eindeutig sein, außerhalb können sie immer über den vorangestellten Package-Namen identifiziert werden.

Damit wird das Problem der Eindeutigkeit auf die Stufe der Packages verlagert, d.h., für eine Internet-Anwendung muss beispielsweise gewährleistet sein, dass Package-Namen eindeutig sind.

Konvention für eindeutige Package-Namen

Sun hat sich für die Packages aus dem eigenen Haus die Namen beginnend mit java, javax oder sun reserviert.

Der Rest der Welt generiert eindeutige Bezeichnungen anhand der eigenen global eindeutigen Internet-Domain-Namen. Nach Java-Konvention kehrt man sie dazu um und schreibt alles durchgängig klein, also z.B.:

Domain Name Package-Name
startet mit
Spezieller Package-Name
MeinName.de de.meinname de.meinname.meinpaket
OurCompany.com com.ourcompany com.ourcompany.project1.dbms
IBM.com com.ibm com.ibm.sf.samples.addressbook

Hierarchie mittels Subpackages

Die letzten beiden Beispiele zeigen hierarchisch angeordnete Packages, wobei ein Subpackage-Name vom übergeordneten Package-Namen durch einen Punkt getrennt wird.

Alle Package-Namen des SanFrancisco Business-Projekts von IBM beginnen z.B. mit com.ibm.sf, wobei Beispiele in einer Gruppe von Subpackages enthalten sind, die mit com.ibm.sf.samples beginnen.

simple vs. fully qualified name

Innerhalb eines Packages müssen die so genannten einfachen Namen (simple names) von Subpackages, Klassen und Interfaces eindeutig sein. Es können also keine zwei Mitglieder eines Packages den gleichen Namen tragen.

Beim vollen Namen (fully qualified name) wird der einfache Name um den vorangestellten vollen Package-Namen ergänzt.

Innerhalb des Subpackages java.awt gibt es z.B. ein Subpackage mit einfachem Namen image bzw. vollem Namen java.awt.image.

Java ist
case-sensitive

Damit ist u.a. ausgeschlossen, dass in java.awt ein weiteres Mitglied, z.B. eine Klasse mit Namen image existiert, wobei allerdings eine Klasse Image erlaubt ist, da Java zwischen Groß- und Kleinbuchstaben unterscheidet.

No-Name/unnamed/Default-Package

Obwohl »gute Sitte«, muss ein Package-Name nicht unbedingt angegeben werden. Fehlt die Angabe, so gehören alle angegebenen Klassen bzw. Interfaces zum default bzw. unnamed Package.

Icon

Da weder die Anzahl der unnamed-Packages noch ihre Abbildung in ein reales Dateisystem festgelegt ist, sollten nur kleine Beispiele bzw. temporärer Code in einem default Package getestet werden.

Für rein lokale Anwendungen kann die erwähnte Namenskonvention sicherlich ignoriert werden, für kommerzielle Anwendungen ist sie sehr hilfreich.10 


Galileo Computing

1.2.2 Java-Code-Struktur  downtop

Compilation-Unit

Java-Code - genauer eine Übersetzungseinheit (Compilation-Unit) - besteht aus folgenden drei Elementen, die exakt in dieser Reihenfolge aufeinander folgen müssen:

Definieren und Importieren von Packages

gp  einer Package-Deklaration (optional)
gp  Import-Anweisungen von anderen Packages (optional)
gp  Klassen bzw. Interface-Definitionen (mindestens eine)

Im folgenden Beispiel wird zuerst ein Package deklariert, gefolgt von zwei Importanweisungen (Erklärung siehe 1.2.3) und einer Klasse:

package com.company.samples.test;
import java.awt.*;
import java.math.BigInteger;
// Anschließend Klassen und Interfaces
class Test1 {
  ...
}

Durch das vorangestellte Schlüsselwort package wird das Package deklariert. Fehlt die Zeile, handelt es sich um das unnamed Package.


Galileo Computing

1.2.3 Zugriff auf Klassen und Import  downtop

Klassen und Interfaces (nicht Subpackages!) im selben Package können sich gegenseitig über den einfachen Namen referenzieren. Darüber hinaus kann auch auf die fundamentalen Klassen und Interfaces von java.lang immer über den einfachen Namen zugegriffen werden.11 

public erklärte Klassen

Um auf Klassen und Interfaces in anderen Packages zugreifen zu können, müssen diese dort public erklärt sein:

public class Test1 { ... }

Für den Zugriff auf public erklärte Klassen anderer Packages muss per Default der volle Name verwendet werden.

Import: Vereinfachter Zugriff auf Klassen anderer Packages

Da dies auf Dauer mühselig ist, kann mittels der Importanweisungen auf alle (oder nur eine) Klasse(n) bzw. Interface(s) des angegebenen Packages auch über den einfachen Namen zugegriffen werden.

Im Beispiel von 1.2.2 werden mit Hilfe des Schlüsselwortes import und des Metasymbols * 12  alle Klassen und Interfaces aus java.awt importiert, gefolgt vom Import genau einer Klasse BigInteger aus java.math.

Führt der Import allerdings zu Namenskollisionen, da in zwei Packages die einfachen Namen gleich sind, muss der Konflikt durch Angabe des vollen Namens beseitigt werden.13 


Galileo Computing

1.2.4 Dateiorganisation und Kompilierung  downtop

Icon

Die Abbildung des Package-Konzepts in ein Verzeichnis- und Dateisystem ist natürlich vom Betriebssystem abhängig. In Java gibt es hierzu die folgenden Regeln, die aber nicht unmittelbar zum Sprachkern zählen und somit durchaus Ausnahmen zulassen:

Regeln zu Packages,
compilation unit, Klassen und Dateisystem

1. Eine Übersetzungseinheit kann beliebig viele Klassen und Interfaces enthalten, wovon jedoch höchstens eine public erklärt werden kann.
2. Eine Übersetzungseinheit wird in einer Datei mit der Extension .java abgespeichert.
3. Der Name der Datei muss den Namen der Klasse bzw. des Interfaces haben, die public erklärt wird. Gibt es keine, kann der Name frei gewählt werden.
4. Nach dem Kompilieren der Übersetzungseinheit wird der Byte-Code jeder Klasse bzw. jedes Interfaces in eine separate Datei mit Namen der Klasse bzw. des Interfaces und der Extension .class abgespeichert.
5. Die Package-Hierarchie wird in eine entsprechende Verzeichnishierarchie aufgelöst, d.h., die .java- bzw. .class-Dateien befinden sich in Unterverzeichnissen, die den Package-Namen abbilden.14

Nur die vierte Regel muss von allen Java-Entwicklungsumgebungen eingehalten werden. Die anderen gelten für Entwicklungsumgebungen, die dateibasierend sind und zur Speicherung des Programmcodes keine Datenbank verwenden.15 

Entsprechend der fünften Regel liegt im Beispiel 1.2.2 die Datei Test1.class des Packages com.company.samples.test bei Windows im Unterverzeichnis com\company\samples\test bzw. bei Unix im Unterverzeichnis com/company/samples/test.

Diese Regel besagt nicht, dass die .java-Dateien im selben Verzeichnis wie die zugehörigen .class-Dateien liegen müssen. Denn die Unterverzeichnisse können für .java- und .class-Dateien verschiedene Ausgangsverzeichnisse (Root-Verzeichnisse) haben.   16 

Wäre die Klasse Test1 public erklärt, müsste die Übersetzungseinheit, die den Java-Code enthält, nach der dritten Regel in einer Datei Test1.java abgespeichert sein.


Galileo Computing

1.2.5 Ausführen einer Applikation  downtop

Dem Java-Interpreter, z.B. java, braucht nicht angegeben zu werden, wo sich die Klassen der Plattform (J2SE) bzw. der Extentions befinden.

Icon

Für alle anderen Klassen, die zur Applikation gehören, sind folgende Regeln zu beachten. Der Interpreter sucht

Regeln zur
Ausführung von Applikationen

1. per default innerhalb des aktuellen Verzeichnisses die Klassen des unnamed Packages.
2. per default unterhalb des aktuellen Verzeichnisses in einem Unterverzeichnis, das dem Package-Namen entspricht, nach den Klassen des Packages.
3. unterhalb der Verzeichnisse, die entweder mittels der Umgebungsvariable CLASSPATH (des Betriebssystems) oder alternativ der Option -classpath (des Interpreters) gesetzt werden.

Die flexibelste Methode ist wohl, mittels -classpath beim Aufruf die Unterverzeichnisse anzugeben. Die Angaben in CLASSPATH werden damit überschrieben bzw. ersetzt.

Aufruf einer
Applikation unter Windows

Werden z.B. nur Klassen aus dem Package j2buch.kap1 benötigt, wobei die Klasse j2buch.kap1.Test1 ausgeführt werden soll, dann kann die Klasse Test1 nach der zweiten Regel mittels

    C:\JB\classes> java j2buch.kap1.Test1

aufgerufen werden. Dies setzt voraus, dass sich Test1 im Unterverzeichnis C:\JB\classes\j2buch\kap1 befindet.

Nach der dritten Regel kann Test1 auch mittels

    java -classpath C:\JB\classes j2buch.kap1.Test1

aus jedem beliebigen Verzeichnis aufgerufen werden.

Eine weitere Möglichkeit ist die, alle benötigten Klassen in eine JAR-Datei test.jar zu packen, die Test1 als Main-Class-Attribut enthält, und diese mittels der Option -jar auszuführen, z.B.:

    java -jar C:\JB\test.jar

Ausnahme (Exception):

Abbruch der normalen Programmausführung durch Exceptions

Fehler, die nicht durch den Compiler abgefangen werden können, d.h. erst bei der Ausführung in der JVM auftreten, werden durch »Auslösen einer Ausnahme« (throwing an exception) von der JVM signalisiert.

Diese Ausnahme kann dann entweder im Programm durch einen entsprechenden Exception-Handler behandelt, d.h. abgefangen werden, oder die JVM bricht die Programmausführung mit einer detaillierten Fehlermeldung (Art/Ort der Exception) ab.


Galileo Computing

1.3 Primitive Datentypen  downtop

Acht primitive Typen

Acht so genannte primitive Datentypen sind Bestandteil der Sprache, werden also vom Compiler direkt erkannt. Hierzu zählen ein logischer (boolean) Typ, ein Zeichentyp sowie sechs numerische Typen.

Daneben gibt es noch Referenz-, Klassen- und Array-Typen.

Integraler Typ

Zeichen und ganze Zahlen werden unter dem Begriff integraler Typ, float und double unter Floating-point-Typ zusammengefasst.

Die primitiven Datentypen sind in der nachfolgenden Tabelle kurz zusammengestellt:

Tabelle 1.2   Primitive Typen
Type Wertebereich Default-Wert Bit-Größe Anmerkung
boolean true, false false 1
char \u0000 .. \uFFFF \u0000 16 unsigned
byte -27   .. 2 -1 0 8
short -215   .. 215  -1 0 16
int -231   .. 231  -1 0 32
long -263  .. 263  -1 0 64
float Float.MIN_VALUE ..
Float.MAX_VALUE,
Float.NaN,
Float.NEGATIVE_INFINITY,
Float.POSITIVE_INFINITY
0.0 32 Darstellbare
Werte:
±1.402e-45 ..
±3.402e38
double Double.MIN_VALUE ..
Double.MAX_VALUE,
Double.NaN,
Double.NEGATIVE_INFINITY,
Double.POSITIVE_INFINITY
0.0 64 Darstellbare
Werte:
±4.94e-324 ..
±1.79e308

Gegenüber C/C++ gibt es einen eigenen boolean-Typ, der eine Umwandlung von/nach Integer nicht zulässt, d.h., 0 oder 1 werden als Ersatz für false und true nicht akzeptiert (siehe 1.5).

Immer mit Vorzeichen (signed)

Alle numerischen Typen sind signed, d.h. erlauben Vorzeichen und haben eine fest definierte Größe, unabhängig von der Maschine bzw. dem Betriebssystem.


Galileo Computing

1.3.1 Regeln zu Operationen mit Zahlen  downtop

Icon

Nachfolgend die wichtigsten Regeln zu Zahlen-Operationen:

Over- bzw. Underflow bei Integer-Werten

1. Bei Integer-Arithmetik stellt weder der Compiler noch die JVM sicher, dass der Wertebereich ausreicht. Ist das Ergebnis außerhalb des Wertebereichs, ist es schlichtweg falsch.

ArithmeticException

2. Nur eine Division durch Null bzw. Modulo Null erzeugt eine ArithmeticException (Ausnahme siehe 1.2.5).

Wertüberschreitung bei Floating-Point-Typen

3. Floating-Point-Typen kennen die Werte »nicht definiert«, d.h. NaN (Not-a-Number) sowie ±Unendlich, d.h. NEGATIVE_INFINITY bzw. POSITIVE_INFINITY, die Überschreitungen des Wertebereichs abfangen.
4. Eine Operation mit einem »nicht definierten« Wert ergibt kein Ergebnis im normalen Wertebereich.
5. Eine Operation mit NaN führt immer zum Ergebnis NaN.

Beispiel17  18 

Tabelle 1.3   Ergebnisse spezieller Floating-Point-Berechnungen
Berechnung von Ergebnis Anmerkung
-0.0 / 0.0 NaN Undefiniert, d.h. NaN
1.0 / -0.0 NEGATIVE_INFINITY es gibt eine negative Null
(0.0 / 0.0) * (1.0 / 0.0) NaN NaN bleibt immer bestehen
0.0 == -0.0 true -0.0 ist gleich 0.0 17 
-0.0/0.0 == -0.0/0.0 false NaN ist mit nichts gleich18 


Galileo Computing

1.4 Lexikalische Grundlagen  downtop

Aufbauend auf einem für Java erlaubten Zeichensatz - dem Unicode - besteht Java-Source-Code aus folgenden atomaren Elementen:

Bestandteile eines
Java-Programms

gp  Whitespaces (Zwischenräume)
gp  Comments (Kommentare)
gp  Identifier (Bezeichner bzw. Namen)
gp  Separators (Trennzeichen)
gp  Literals (Literale)
gp  Keywords (Schlüsselworte)
gp  Operators (Operatoren)

Galileo Computing

1.4.1 Unicode  downtop

Java verwendet als erlaubten Zeichensatz Unicode, womit es sich bereits von C/C++ in seinen Grundlagen unterscheidet.

Eine Sprache für Internet-Applikationen muss neben dem Standardzeichensatz für Englisch auch andere Zeichensätze wie Arabisch, Katakana, Griechisch etc. darstellen können.

Unicode-Tabelle

Da Unicode-Zeichen 16-Bit codiert sind, können prinzipiell 65.536 (=216   ) Zeichen dargestellt werden. Die Unicode-Tabelle ist in Bereiche für verschiedene Sprachen eingeteilt.

Kompatibel zu
ASCII, ISO-Latin-1

Im Tabellenbereich von 0...127 bzw. 0...255 ist Unicode identisch mit dem ASCII- bzw. ISO-Latin-1-Zeichensatz. Für diese Zeichen kann man also das oberste der beiden Bytes ignorieren.19 

Da man selten eine Tastatur mit 65.536 Tasten zur Eingabe benutzt, gibt es eine spezielle Escape-Sequenz für Unicode-Zeichen (siehe 1.4.6).


Galileo Computing

1.4.2 Whitespace  downtop

Java ist frei formatierbar
(free-form)

Die Anzahl Leerstellen, Tabulatoren oder Zeilenumbrüche (Linefeed bzw. Carriage-Return) zwischen Token (Symbolen) können beliebig gewählt werden, sie werden ohnehin bei der lexikalischen Analyse durch den Compiler entsorgt.


Galileo Computing

1.4.3 Kommentare  downtop

Es gibt drei Arten von Kommentaren, die von C++ adaptiert wurden:

gp  einzeilige:
           i=i+1; // oder i++; oder ++i; oder i+=1;
gp  mehrzeilige:
           /* Arrays können im C/C++ oder im
              Java-Stil deklariert werden */
           int iarr[]; byte[] barr;
gp  mehrzeilige Kommentare, die mit /** starten. Hieraus kann mit Hilfe von javadoc eine HTML-Dokumentation generiert werden. javadoc versteht einfache HTML-Formatierungen sowie mit @ beginnende Tags, d.h. Hinweise auf Text mit festgelegter Bedeutung:
           /** money factory class - a Pattern
           @author BG
           @version 0.9 
           <U>code name</U>: <B>W98 Green Banana</B>
           */

Galileo Computing

1.4.4 Identifier  downtop

Ein Identifier ist ein vom Programmierer wählbarer Name für Variablen, Marken, Methoden und Klassen mit folgenden Restriktionen:

gp  Das erste Zeichen muss ein (Unicode) Buchstabe, ein Unterstreichungsstrich _ oder ein Währungssymbol $, £ bzw. ¥ sein.
gp  Ab dem zweiten Zeichen sind noch zusätzlich Ziffern erlaubt.
gp  Schlüsselwörter (siehe Tabelle 1.5) sind nicht erlaubt.

Nachfolgend einige zulässige (erste Zeile) bzw. unzulässige Identifier:

_i__  £4  äß  _123  Õre
2i  ab/1  B-1  ab!  a%b  #a  goto

Galileo Computing

1.4.5 Separator   downtop

Java verwendet neun Zeichen als Trennzeichen mit besonderer Bedeutung, wobei der häufigste Separator wohl das Semikolon ist, das Anweisungen abschließt. Die restlichen sind verschiedene Arten von Klammern, Komma und Punkt:

; , . ( ) { } [ ]

Galileo Computing

1.4.6 Literale  downtop

Literale:
konstante Werte

Im Gegensatz zu einem Identifier repräsentiert ein Literal einen konstanten Wert eines bestimmten Typs. Literale können in Ausdrücken, Zuweisungen und als Argumente beim Methoden-Aufruf verwendet werden.

Es gibt Literale zu den primitiven Typen, zur Klasse String, das null-Literal sowie noch Literale der Klasse Class, die im Folgenden kurz vorgestellt werden:

Logische Literale

Die einzigen Literale vom Typ boolean sind true und false.

        boolean ok = false;

Zeichen-Literale

Zeichen-Literale müssen immer in Hochkommata eingeschlossen werden.

Hexadezimale Eingabe von Zeichen

Für Zeichen, die nicht mittels Tastatur eingegeben werden können, wird die Unicode-Tabellenposition in Form von (maximal) vier hexadezimalen Ziffern 0..F (Basis 16) mit dem Präfix \u eingegeben.20  Die Hex-Ziffern können groß A..F oder klein a..f geschrieben werden.

Für die Eingabe von speziellen Zeichen gibt es in Java wie C/C++ die nachfolgenden Escape-Codes:

Tabelle 1.4   Escape-Codes für Sonderzeichen
Escape-Code Zeichen
'\''
Hochkomma (single quote)
'\"'
Anführungszeichen (double quote)
'\\'
Schrägstrich nach hinten (backslash)
'\b'
Schritt zurück (backspace)
'\t'
Tabulator (tab)
'\n'
Zeilenschaltung (linefeed/newline)
'\r'
Wagenrücklauf ( carriage return)
'\f'
Seitenschaltung (form feed)

Mit Hilfe des Escape-Codes lassen sich Unicode-Zeichen eingeben:

char c1='\u00c4';
System.out.print(c1=='Ä');  // :: true

Integrale Literale

Eingabe von ganzen Zahlen

Die als integrale Literale bezeichneten ganzen Zahlen vom Typ int oder long können dezimal, hexadezimal oder sogar oktal eingegeben werden, wobei die hexadezimale Eingabe mit 0x oder 0X und die oktale mit 0 beginnen muss.

Alle Literale sind per default vom Typ int. Wird an das Literal (das Suffix) l oder L angehängt, ist es vom Typ long.

int i= 0x10+10+010;               // 16+10+8
System.out.print(i+" "+0x10L);    // :: 34 16

Floating-Point-Literale

Eingabe von
Dezimalzahlen

Ein Floating-Point-Literal vom Typ float oder double ist eine Zahl mit Dezimalpunkt und/oder einem angehängten Exponent und/oder einem Suffix f oder F für float bzw. d oder D für double. Fehlt das Suffix, ist das Literal per default vom Typ double.

Der Exponent beginnt mit E oder e, gefolgt von einem optionalen Vorzeichen und dem Exponenten (möglicher Wertebereich siehe Tabelle 1.2). Damit sind alle nachfolgenden Zahlen gültige Floating-Point-Literale:

           0. .0     +1f    -6e+1 
    7.1E-1F

Dabei ist die letzte Zahl gleich 0.71 und vom Typ float .

String-Literale

Strings sind Zeichensequenzen und werden immer in Anführungszeichen (double quotes) gesetzt. Innerhalb des Strings können die einzelnen Unicode-Zeichen (wie bei Zeichen-Literalen) per Escape-Code oder hexadezimal eingegeben werden.

Die Anweisung

System.out.print("\"\u00c4\tÖ\tÜ\"\nNeue 
Zeile");

gibt (unter Windows) zwei Zeilen auf der Konsole aus:21 

"Ä    Ö    Ü"
Neue Zeile

String-Literale sind Objekte

String-Literale sind keine Werte vom primitiven Typ, sondern sind eine kurze elegante Art, Objekte der Klasse String anzulegen.

Somit können auf String-Literale alle Instanz-Methoden der Klasse String angewendet werden, wie z.B. die Methode length(), die die Länge des Strings zurückgibt:

System.out.print("".length()); 
// :: 0

Literal null

null: Kein Objekt referenziert

Neben den Variablen vom primitiven Typ gibt es noch Referenzvariablen, die auf Objekte von Klassen zeigen. Um anzuzeigen, dass eine Referenzvariable auf kein Objekt zeigt, wird das Literal null (ein Schlüsselwort) verwendet.

String s= null; // s zeigt auf kein String-Objekt
System.out.println(s);           // :: null
System.out.println(s.length());  // Exception

NullPointerException

Die letzte Code-Zeile führt nicht zu einem Compilerfehler, sondern bei Ablauf des Programms zu einer NullPointerException, da von einem nicht existierenden Objekt keine Länge abgefragt werden kann.

Literale zur Klasse Class 22 

Class-Literale
enthalten Klassen-Informationen

Die Klasse Class enthält Objekte zu allen Datentypen (inkl. sich selbst). Durch Anhängen von .class hinter einem beliebigen Typ kann man ein zu diesem Typ zugehöriges Literal vom Typ Class schaffen.

Class intTyp= int.class;

Dies kann u.a. zur Untersuchung von Klassen verwendet werden. Das nachfolgende Code-Fragment gibt alle Methoden der Klasse String aus (die Klasse Method ist aus Package java.lang.reflect zu importieren):

Class stringType= String.class;
Method[] methodArr= stringType.getMethods();
for (int i=0; i<methodArr.length; i++)
  System.out.println(methodArr[i]);

Galileo Computing

1.4.7 Schlüsselwort  downtop

Keyword:
Identifier mit fester Bedeutung

Schlüsselwörter sind vordefinierte Identifier mit einer speziellen Bedeutung. Sie dürfen deshalb auch nicht als normale Identifier verwendet werden.

Es gibt zurzeit 59 Schlüsselwörter, die anhand ihrer Funktion in Kategorien eingeteilt werden können. Zum Beispiel gibt es für Bedingungen die Schlüsselworte if, else und switch und eine Gruppe von elf Schlüsselwörtern, die zwar reserviert (®) sind, aber nicht benutzt werden.

Tabelle 1.5   Java-Schlüsselwörter
abstract const ® float int protected throw
boolean continue for interface public throws
break default future ® long rest ® transient
byte do generic ® native return true
byvalue ® double goto ® new short try
case else if null static var ®
cast ® extends implements operator ® super void
catch false import outer ® switch volatile
char final inner ® package synchronized while
class finally instanceof private this


Galileo Computing

1.4.8 Operatoren  downtop

Operatoren sind spezielle Symbole für Operationen auf Operanden. Je nach Anzahl der Operanden, auf die der Operator angewendet wird, unterscheidet man unäre und binäre Operatoren sowie einen ternären Operator.

Java kennt 37 Operatoren, die anhand ihrer Funktion klassifiziert werden, wie z.B. arithmetische oder logische Operatoren. Operatoren werden in Kapitel 2 behandelt.


Galileo Computing

1.5 Konvertierung primitiver Typen  down